Build transitive dev-dependencies when needed
authorMatt Brubeck <mbrubeck@limpet.net>
Mon, 26 Sep 2016 21:13:49 +0000 (14:13 -0700)
committerMatt Brubeck <mbrubeck@limpet.net>
Tue, 27 Sep 2016 22:22:59 +0000 (15:22 -0700)
When running `cargo test -p foo` where `foo` is a crate in the current
workspace, build and link `foo`'s dev-dependencies. Fixes #860.

src/cargo/ops/cargo_compile.rs
src/cargo/ops/cargo_generate_lockfile.rs
src/cargo/ops/cargo_output_metadata.rs
src/cargo/ops/resolve.rs
tests/workspaces.rs

index 25b96891787f3acab461981f3bb9d8ddca91223e..512117ed8ba7ec44387e9d4df4b54bbd91b64a26 100644 (file)
@@ -28,7 +28,7 @@ use std::sync::Arc;
 
 use core::registry::PackageRegistry;
 use core::{Source, SourceId, PackageSet, Package, Target};
-use core::{Profile, TargetKind, Profiles, Workspace};
+use core::{Profile, TargetKind, Profiles, Workspace, PackageIdSpec};
 use core::resolver::{Method, Resolve};
 use ops::{self, BuildOutput, ExecEngine};
 use sources::PathSource;
@@ -97,7 +97,8 @@ pub fn resolve_dependencies<'a>(ws: &Workspace<'a>,
                                 source: Option<Box<Source + 'a>>,
                                 features: Vec<String>,
                                 all_features: bool,
-                                no_default_features: bool)
+                                no_default_features: bool,
+                                spec: &'a [String])
                                 -> CargoResult<(PackageSet<'a>, Resolve)> {
 
     let mut registry = try!(PackageRegistry::new(ws.config()));
@@ -128,9 +129,13 @@ pub fn resolve_dependencies<'a>(ws: &Workspace<'a>,
         }
     };
 
+    let specs = try!(spec.iter().map(|p| PackageIdSpec::parse(p))
+                                .collect::<CargoResult<Vec<_>>>());
+
     let resolved_with_overrides =
             try!(ops::resolve_with_previous(&mut registry, ws,
-                                            method, Some(&resolve), None));
+                                            method, Some(&resolve), None,
+                                            &specs));
 
     let packages = ops::get_resolved_packages(&resolved_with_overrides,
                                               registry);
@@ -164,9 +169,8 @@ pub fn compile_ws<'a>(ws: &Workspace<'a>,
         try!(generate_targets(root_package, profiles, mode, filter, release));
     }
 
-    let (packages, resolve_with_overrides) = {
-        try!(resolve_dependencies(ws, source, features, all_features, no_default_features))
-    };
+    let (packages, resolve_with_overrides) =
+        try!(resolve_dependencies(ws, source, features, all_features, no_default_features, spec));
 
     let mut pkgids = Vec::new();
     if spec.len() > 0 {
index 6a6d198b35b1c615466b5c903b92c33f6b17480f..25e2204ba9e12dcc94dfff963b17188a23ea6e55 100644 (file)
@@ -19,7 +19,7 @@ pub fn generate_lockfile(ws: &Workspace) -> CargoResult<()> {
     let mut registry = try!(PackageRegistry::new(ws.config()));
     let resolve = try!(ops::resolve_with_previous(&mut registry, ws,
                                                   Method::Everything,
-                                                  None, None));
+                                                  None, None, &[]));
     try!(ops::write_pkg_lockfile(ws, &resolve));
     Ok(())
 }
@@ -78,7 +78,8 @@ pub fn update_lockfile(ws: &Workspace, opts: &UpdateOptions)
                                                   ws,
                                                   Method::Everything,
                                                   Some(&previous_resolve),
-                                                  Some(&to_avoid)));
+                                                  Some(&to_avoid),
+                                                  &[]));
 
     // Summarize what is changing for the user.
     let print_change = |status: &str, msg: String| {
index 035c7350fe960046ceb0c75c377b83916440271b..131b93825db8c414eed214de6c79f4f70b20f60e 100644 (file)
@@ -47,7 +47,8 @@ fn metadata_full(ws: &Workspace,
                                               None,
                                               opt.features.clone(),
                                               opt.all_features,
-                                              opt.no_default_features));
+                                              opt.no_default_features,
+                                              &[]));
     let (packages, resolve) = deps;
 
     let packages = try!(packages.package_ids()
index 7ab3d1b4c12dc8fce63ac50615c6788a7ced2594..5c8b0d89a4a10bdc2b0a03cd3588e8b6eccc60f5 100644 (file)
@@ -1,6 +1,6 @@
 use std::collections::{HashMap, HashSet};
 
-use core::{PackageId, SourceId, Workspace};
+use core::{PackageId, PackageIdSpec, SourceId, Workspace};
 use core::registry::PackageRegistry;
 use core::resolver::{self, Resolve, Method};
 use ops;
@@ -16,7 +16,7 @@ pub fn resolve_ws(registry: &mut PackageRegistry, ws: &Workspace)
     let prev = try!(ops::load_pkg_lockfile(ws));
     let resolve = try!(resolve_with_previous(registry, ws,
                                              Method::Everything,
-                                             prev.as_ref(), None));
+                                             prev.as_ref(), None, &[]));
 
     // Avoid writing a lockfile if we are `cargo install`ing a non local package.
     if ws.current_opt().map(|pkg| pkg.package_id().source_id().is_path()).unwrap_or(true) {
@@ -38,7 +38,8 @@ pub fn resolve_with_previous<'a>(registry: &mut PackageRegistry,
                                  ws: &Workspace,
                                  method: Method,
                                  previous: Option<&'a Resolve>,
-                                 to_avoid: Option<&HashSet<&'a PackageId>>)
+                                 to_avoid: Option<&HashSet<&'a PackageId>>,
+                                 specs: &[PackageIdSpec])
                                  -> CargoResult<Resolve> {
     // Here we place an artificial limitation that all non-registry sources
     // cannot be locked at more than one revision. This means that if a git
@@ -67,7 +68,8 @@ pub fn resolve_with_previous<'a>(registry: &mut PackageRegistry,
         if let Method::Required { .. } = method {
             assert!(previous.is_some());
             if let Some(current) = ws.current_opt() {
-                if member.package_id() != current.package_id() {
+                if member.package_id() != current.package_id() &&
+                   !specs.iter().any(|spec| spec.matches(member.package_id())) {
                     continue;
                 }
             }
index 08be87bf4cf70924191299855fbe95b14f97a6e7..42ac608da464115b5134bf70ec607418bd3160f1 100644 (file)
@@ -962,3 +962,51 @@ fn you_cannot_generate_lockfile_for_empty_workspaces() {
 error: you can't generate a lockfile for an empty workspace.
 "));
 }
+
+#[test]
+fn workspace_with_transitive_dev_deps() {
+    let p = project("foo")
+        .file("Cargo.toml", r#"
+            [project]
+            name = "foo"
+            version = "0.5.0"
+            authors = ["mbrubeck@example.com"]
+
+            [dependencies.bar]
+            path = "bar"
+
+            [workspace]
+        "#)
+        .file("src/main.rs", r#"fn main() {}"#)
+        .file("bar/Cargo.toml", r#"
+            [project]
+            name = "bar"
+            version = "0.5.0"
+            authors = ["mbrubeck@example.com"]
+
+            [dev-dependencies.baz]
+            path = "../baz"
+        "#)
+        .file("bar/src/lib.rs", r#"
+            pub fn init() {}
+
+            #[cfg(test)]
+
+            #[test]
+            fn test() {
+                extern crate baz;
+                baz::do_stuff();
+            }
+        "#)
+        .file("baz/Cargo.toml", r#"
+            [project]
+            name = "baz"
+            version = "0.5.0"
+            authors = ["mbrubeck@example.com"]
+        "#)
+        .file("baz/src/lib.rs", r#"pub fn do_stuff() {}"#);
+    p.build();
+
+    assert_that(p.cargo("test").args(&["-p", "bar"]),
+                execs().with_status(0));
+}